<?php

class QQTranslator {

    // =========================================================================
    // 成员变量
    // =========================================================================

    /**
     * 非必要不更改！
     *
     * API 支持就近地域接入，本产品就近地域接入域名为 tmt.tencentcloudapi.com ，
     * 也支持指定地域域名访问，例如广州地域的域名为 tmt.ap-guangzhou.tencentcloudapi.com 。
     *
     * 推荐使用就近地域接入域名。根据调用接口时客户端所在位置，会自动解析到最近的某个具体地域的服务器。
     * 例如在广州发起请求，会自动解析到广州的服务器，效果和指定 tmt.ap-guangzhou.tencentcloudapi.com 是一致的。
     *
     * 注意：对时延敏感的业务，建议指定带地域的域名。
     * 注意：域名是 API 的接入点，并不代表产品或者接口实际提供服务的地域。
     * 产品支持的地域列表请在调用方式/公共参数文档中查阅，接口支持的地域请在接口文档输入参数中查阅。
     *
     * 目前支持的域名列表为：
     * https://cloud.tencent.com/document/api/551/15614#1.-.E6.9C.8D.E5.8A.A1.E5.9C.B0.E5.9D.80
     * @var string
     */
    private $endpoint = "tmt.tencentcloudapi.com";

    /**
     * 地域参数，用来标识希望操作哪个地域的数据。
     * 接口接受的地域取值参考接口文档中输入参数公共参数 Region 的说明。
     * 注意：某些接口不需要传递该参数，接口文档中会对此特别说明，此时即使传递该参数也不会生效。
     * 本产品所有接口 Region 字段的可选值如下表所示：
     * https://cloud.tencent.com/document/api/551/15615#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8
     * @var string
     */
    private $region = "ap-hongkong";

    /**
     * 操作的 API 的版本。取值参考接口文档中入参公共参数 Version 的说明。
     * 例如云服务器的版本 2017-03-12。
     * @var string
     */
    private $version = "2018-03-21";

    /**
     * 项目ID，可以根据「控制台-账号中心-项目管理」中的配置填写，
     * 如无配置请填写默认项目ID:0
     * @var int
     */
    private $projectId = 0; // 项目ID

    /**
     * 在 云API密钥 上申请的标识身份的 SecretId。
     * 用于标识 API 调用者身份。
     * 一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名 Signature。
     * @var string
     */
    private $secretId = "";

    /**
     * a 密钥 that 用于加密「签名字符串」，以及在服务器端验证「签名字符串」。
     * @var string
     */
    private $secretKey = "";

    // -------------------------------------------------------------------------

    /**
     * 待翻译的文本，文本统一使用utf-8格式编码，非utf-8格式编码字符会翻译失败，请传入有效文本。
     * html标记等非常规翻译文本可能会翻译失败。
     * 单次请求的文本长度需要低于6000字符。
     * 示例值：hello
     * @var string
     */
    private $sourceLanguage = "auto"; // 源语言

    /**
     * 目标语言
     * 各源语言的目标语言支持列表如下：
     * https://cloud.tencent.com/document/api/551/15619#2.-.E8.BE.93.E5.85.A5.E5.8F.82.E6.95.B0
     * @var string
     */
    private $targetLanguage = "zh"; // 目标语言

    /**
     * 用来标记不希望被翻译的文本内容，如句子中的特殊符号、人名、地名等；
     * 只适用于「单文本翻译」，不适用于「批量文本翻译」。
     * 每次请求只支持配置一个不被翻译的单词；
     * 仅支持配置人名、地名等名词，不要配置动词或短语，否则会影响翻译结果。
     * @var string
     */
    private $untranslatedText = "";

    /**
     * 需要使用的术语库列表
     * 示例值：["144aed8218c911eb97b88eb0fc7321d4"]
     *
     * 术语库功能支持用户上传术语对机器翻译结果进行干预，快速提升翻译质量，可在文本翻译和批量文本翻译中使用。
     * 支持语种为中-英、英-中。
     * 最大支持创建5个术语库，单个术语库最大支持10000条术语。
     * @var array
     */
    private $termRepoIDList = array(); // the 术语库列表 that 需要用到的

    /**
     * 需要使用的例句库列表
     * 示例值：["b77f64e518ec11ebb2aabf416d88df30"]
     *
     * 例句库功能支持用户上传例句对机器翻译结果进行干预，快速提升翻译质量，可在文本翻译和批量文本翻译中使用。
     * 支持语种为中-英、英-中。
     * 最大支持创建5个例句库，单个例句库最大支持10000条例句。
     * @var array
     */
    private $sentRepoIDList = array(); // the 例句库列表 that 需要用到的

    // =========================================================================
    // 公共成员函数
    // =========================================================================

    // -------------------------------------------------------------------------
    // the 函数 that 为成员变量赋值
    // -------------------------------------------------------------------------

    public function setEndpoint($endpoint) {
        $this->endpoint = $endpoint;
    }

    public function setRegion($region) {
        $this->region = $region;
    }

    public function setProjectId($projectId) {
        $this->projectId = $projectId;
    }

    public function setVersion($version) {
        $this->version = $version;
    }

    public function setSecretId($secretId)
    {
        $this->secretId = $secretId;
    }

    public function setSecretKey($secretKey)
    {
        $this->secretKey = $secretKey;
    }

    public function setUntranslatedText($untranslatedText) {
        $this->untranslatedText = $untranslatedText;
    }

    public function setTermRepoIDList($termRepoIDList) {
        $this->termRepoIDList = $termRepoIDList;
    }

    public function setSentRepoIDList($sentRepoIDList) {
        $this->sentRepoIDList = $sentRepoIDList;
    }

    // -------------------------------------------------------------------------
    // the 函数 that 处理翻译请求
    // -------------------------------------------------------------------------

    /**
     * 一次翻译一个句子
     * 默认接口请求频率限制：5次/秒。
     * @param $sourceText string 待翻译的内容
     * @param $sourceLanguage string 源语言。默认值为 auto，自动识别源语言
     * @param $targetLanguage string 目标语言。默认值为 zh，指定目标语言为简体中文
     * @return string 请求成功，返回翻译后的内容；请求失败，返回错误提示。
     */
    public function translateString(string $sourceText, string $sourceLanguage = "auto", string $targetLanguage = "zh") {

        $secretId = $this->secretId;
        $secretKey = $this->secretKey;
        $projectId = $this->projectId;
        $region = $this->region;
        $version = $this->version;
        $untranslatedText = $this->untranslatedText;
        $termRepoIDList = $this->termRepoIDList;
        $sentRepoIDList = $this->sentRepoIDList;

        $action = "TextTranslate";

        // 当前 UNIX 时间戳，可记录发起 API 请求的时间。
        // 例如1529223702，如果与当前时间相差过大，会引起签名过期错误。
        $timestamp = strval(time());

        // 随机正整数，与 Timestamp 联合起来，用于防止重放攻击。
        $nonce = strval(rand());

        // ---------------------------------------------------------------------

        $params = array(
            'Signature'  => '',
            'Action'     => $action,
            'Region'     => $region,
            'ProjectId'  => $projectId,
            'SecretId'   => $secretId,
            'Timestamp'  => $timestamp,
            'Nonce'      => $nonce,
            'Version'    => $version,
            'Source'     => $sourceLanguage,
            'Target'     => $targetLanguage,
            'SourceText' => $sourceText,
        );

        if (!empty($untranslatedText)) {
            $params['UntranslatedText'] = $untranslatedText;
        }

        $counter = 0;
        if (count($termRepoIDList) > 0) {
            foreach ($termRepoIDList as $termRepoID) {
                $itemName = 'TermRepoIDList.' . $counter;
                $params[$itemName] = $termRepoID;
                $counter += 1;
            }
        }

        $counter = 0;
        if (count($sentRepoIDList) > 0) {
            foreach ($sentRepoIDList as $sentRepoId) {
                $itemName = 'SentRepoIDList.' . $counter;
                $params[$itemName] = $sentRepoId;
                $counter += 1;
            }
        }

        $params['Signature'] = $this->getReqSign($params, $secretKey);

        // ---------------------------------------------------------------------

        $url = 'https://' . $this->endpoint;
        $response = $this->doHttpPost($url, $params);
        $responseArray = json_decode($response, true);

        if (isset($responseArray['Response']['TargetText'])) {
            $targetText = $responseArray["Response"]['TargetText'];
            return $targetText;
        } elseif (isset($responseArray['Response']['Error'])) {
            $errorCode = $responseArray['Response']['Error']['Code'];
            $errorMessage = $responseArray['Response']['Error']['Message'];
            return "获取翻译失败。错误代码：" . $errorCode . "，错误描述：" . $errorMessage;
        } else {
            return "获取翻译失败。未知错误。";
        }

    }

    // -------------------------------------------------------------------------

    /**
     * 一次翻译多个句子
     * 默认接口请求频率限制：5次/秒。
     * @param $sourceTextList array 一个数组，包含多个待翻译的句子
     * @param $sourceLanguage string 源语言。默认值为 auto，自动识别源语言
     * @param $targetLanguage string 目标语言。默认值为 zh，指定目标语言为简体中文
     * @return mixed|string 翻译成功返回一个数组，翻译失败返回一个字符串
     */
    public function translateStrings(array $sourceTextList, string $sourceLanguage = "auto", string $targetLanguage = "zh") {

        $secretId = $this->secretId;
        $secretKey = $this->secretKey;
        $projectId = $this->projectId;
        $region = $this->region;
        $version = $this->version;
        // $untranslatedText = $this->untranslatedText;
        $termRepoIDList = $this->termRepoIDList;
        $sentRepoIDList = $this->sentRepoIDList;

        $action = "TextTranslateBatch";

        // 当前 UNIX 时间戳，可记录发起 API 请求的时间。
        // 例如1529223702，如果与当前时间相差过大，会引起签名过期错误。
        $timestamp = strval(time());

        // 随机正整数，与 Timestamp 联合起来，用于防止重放攻击。
        $nonce = strval(rand());

        // ---------------------------------------------------------------------

        $params = array(
            'Signature' => '',
            'Action'    => $action,
            'Region'    => $region,
            'ProjectId' => $projectId,
            'SecretId'  => $secretId,
            'Source'    => $sourceLanguage,
            'Target'    => $targetLanguage,
            'Timestamp' => $timestamp,
            'Nonce'     => $nonce,
            'Version'   => $version,
        );

        $counter = 0;
        foreach ($sourceTextList as $sourceText) {
            $itemName = 'SourceTextList.' . $counter;
            $params[$itemName] = $sourceText;
            $counter += 1;
        }

        $counter = 0;
        if (count($termRepoIDList) > 0) {
            foreach ($termRepoIDList as $termRepoID) {
                $itemName = 'TermRepoIDList.' . $counter;
                $params[$itemName] = $termRepoID;
                $counter += 1;
            }
        }

        $counter = 0;
        if (count($sentRepoIDList) > 0) {
            foreach ($sentRepoIDList as $sentRepoId) {
                $itemName = 'SentRepoIDList.' . $counter;
                $params[$itemName] = $sentRepoId;
                $counter += 1;
            }
        }

        $params['Signature'] = $this->getReqSign($params, $secretKey);

        // ---------------------------------------------------------------------

        $url = 'https://' . $this->endpoint;
        $response = $this->doHttpPost($url, $params);
        $responseArray = json_decode($response, true);

        if (isset($responseArray['Response']['TargetTextList'])) {
            return $responseArray['Response']['TargetTextList'];
        } elseif (isset($responseArray['Response']['Error'])) {
            $errorCode = $responseArray['Response']['Error']['Code'];
            $errorMessage = $responseArray['Response']['Error']['Message'];
            return "获取翻译失败。错误代码：" . $errorCode . "，错误描述：" . $errorMessage;
        } else {
            return "获取翻译失败。未知错误。";
        }

    }

    // =========================================================================
    // 私有成员函数
    // =========================================================================

    /**
     * 根据「接口请求参数」和「应用密钥」计算「请求签名」
     * @param $params array 接口请求参数（特别注意：不同的接口，参数对一般不一样，请以具体接口要求为准）
     * @param $appkey string 应用密钥
     * @return string 签名结果
     */
    private function getReqSign($params, $appkey)
    {

        // 1. 对参数排序

        ksort($params);

        // 2. 拼接请求字符串
        // 参数值为原始值而非编码后的值

        $str = '';
        foreach ($params as $key => $value)
        {
            if ($value !== '')
            {
                $str .= $key . '=' . $value . '&';
            }
        }
        $str = rtrim($str, '&');

        // 3. 拼接签名原文字符串
        // 签名原文串的拼接规则为：请求方法 + 请求主机 + 请求路径 + ? + 请求字符串。

        $str = 'POST' . $this->endpoint . '/' . '?' . $str;

        // 4. 生成签名串

        $sign = base64_encode(hash_hmac('sha1', $str, $appkey, true));

        return $sign;

    }

    /**
     * 执行POST请求，并取回响应结果
     * @param $url string 接口请求地址
     * @param $params array 完整接口请求参数（特别注意：不同的接口，参数对一般不一样，请以具体接口要求为准）
     * @return bool|string 返回false表示失败，否则表示API成功返回的HTTP BODY部分
     */
    private function doHttpPost($url, $params)
    {

        $curl = curl_init();

        $response = false;
        do
        {
            // 1. 设置HTTP URL (API地址)

            curl_setopt($curl, CURLOPT_URL, $url);

            // 2. 设置HTTP HEADER (表单POST)

            $head = array(
                'Content-Type: application/x-www-form-urlencoded'
            );
            curl_setopt($curl, CURLOPT_HTTPHEADER, $head);

            // 3. 设置HTTP BODY (URL键值对)

            $body = http_build_query($params);

            curl_setopt($curl, CURLOPT_POST, true);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
            curl_setopt($curl, CURLOPT_HEADER, false);
            curl_setopt($curl, CURLOPT_NOBODY, false);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);

            // 4. 调用API，获取响应结果

            $response = curl_exec($curl);
            if ($response === false)
            {
                $response = false;
                break;
            }

            $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
            if ($code != 200)
            {
                $response = false;
                break;
            }

        } while (0);

        curl_close($curl);

        return $response;

    }

}
